package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumAttributeValueService;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.login.GuiguLogin;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private AlbumAttributeValueMapper albumAttributeValueMapper;

	@Autowired
	private AlbumStatMapper albumStatMapper;

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private RabbitService rabbitService;

	@Autowired
	private RedisTemplate redisTemplate;

	//保存专辑
	//当前操作操作多张表，保证多张表数据一致性，添加事务
	//当前这些表在一个数据库里面，这种事务称为本地事务
	@Transactional
	@Override
	public void saveAlbumInfo(AlbumInfoVo albumInfoVo) {
		//1 添加专辑基本信息 album_info
		AlbumInfo albumInfo = new AlbumInfo();
		// AlbumInfoVo 值--放到 AlbumInfo
		//String albumTitle = albumInfoVo.getAlbumTitle();
		//albumInfo.setAlbumTitle(albumTitle);
		BeanUtils.copyProperties(albumInfoVo,albumInfo);

		//专辑有几个值需要单独设置，前端没有传递过来的
		//TODO  userId 用户id，后面完善
		albumInfo.setUserId(1L);
		// 专辑状态，设置通过
		albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
		//设置收费专辑，免费试听集数，前3集
		String payType = albumInfo.getPayType();
		if(!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(payType)) {
			albumInfo.setTracksForFree(3);
		}
		//调用方法保存
		albumInfoMapper.insert(albumInfo);

		//2 添加专辑下面标签名称和标签值数据 album_attribute_value
		List<AlbumAttributeValueVo> albumAttributeValueVoList =
				albumInfoVo.getAlbumAttributeValueVoList();
		//非空判断
		if(!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
			albumAttributeValueVoList.stream().forEach(albumAttributeValueVo -> {
				AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
				// AlbumAttributeValueVo --  AlbumAttributeValue
				BeanUtils.copyProperties(albumAttributeValueVo,albumAttributeValue);
				//专辑id
				albumAttributeValue.setAlbumId(albumInfo.getId());

				albumAttributeValueMapper.insert(albumAttributeValue);
			});
		}

		//3 添加专辑四个统计数据 播放量，订阅量等 初始值 0 album_stat
		//播放量
		this.saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_PLAY);
		//订阅量
		this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_SUBSCRIBE);
		//购买量
		this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_BROWSE);
		//评论数
		this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_COMMENT);

		//判断专辑是否公开，如果公开，发送mq消息，实现专辑上架
		String isOpen = albumInfo.getIsOpen();
		//	发送上架消息
		if ("1".equals(isOpen)){
			rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,
					                  MqConst.ROUTING_ALBUM_UPPER,
					                  albumInfo.getId());
		}
	}

	//查询专辑列表
	@Override
	public IPage<AlbumListVo> selectAlbumPage(Page<AlbumListVo> pageParam, AlbumInfoQuery albumInfoQuery) {
		return albumInfoMapper.selectUserAlbumPage(pageParam,albumInfoQuery);
	}

	//删除专辑
	@Override
	public void removeAlbumInfo(String albumId) {
		//1 判断当前专辑下面是否包含声音，如果包含不能删除
		// select count(*) from track_info where album_id=?
		LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(TrackInfo::getAlbumId,albumId);
		Long count = trackInfoMapper.selectCount(queryWrapper);
		if(count > 0) {//包含声音，如果包含不能删除
			throw new GuiguException(400, "该专辑下存在未删除声音！");
		}

		//2 如果专辑下面不包含声音，可以删除
		//2.1 删除专辑基本信息
		albumInfoMapper.deleteById(albumId);

		//2.2 删除专辑标签名称和标签值数据
		LambdaQueryWrapper<AlbumAttributeValue> queryWrapper1 =
				                               new LambdaQueryWrapper<>();
		queryWrapper1.eq(AlbumAttributeValue::getAlbumId,albumId);
		albumAttributeValueMapper.delete(queryWrapper1);

		//2.3 删除专辑四个统计数据
		LambdaQueryWrapper<AlbumStat> queryWrapper2 =
				new LambdaQueryWrapper<>();
		queryWrapper2.eq(AlbumStat::getAlbumId,albumId);
		albumStatMapper.delete(queryWrapper2);

		//发送mq消息
		//下架
		rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,
				MqConst.ROUTING_ALBUM_LOWER, albumId);
	}

	//修改-根据专辑id获取专辑数据
	//  album:info:+albumId
	@GuiGuCache(prefix = "album:info:")
	@Override
	public AlbumInfo getAlbumInfo(Long albumId) {
		return this.getAlbumInfoData(albumId);
	}

	//根据专辑id查询mysql方法
	private AlbumInfo getAlbumInfoData(Long albumId) {
		//1 根据专辑id获取专辑基本信息
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);

		if(albumInfo != null) {
			//2 根据专辑id获取标签数据
			LambdaQueryWrapper<AlbumAttributeValue> wrapper = new LambdaQueryWrapper<>();
			wrapper.eq(AlbumAttributeValue::getAlbumId,albumId);
			List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueMapper.selectList(wrapper);

			//3 把获取标签数据list集合封装到专辑对象里面
			albumInfo.setAlbumAttributeValueVoList(albumAttributeValueList);
		}
		return albumInfo;
	}

	//修改
	@Override
	public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {
		//根据专辑id修改专辑基本信息
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		BeanUtils.copyProperties(albumInfoVo,albumInfo);
		albumInfoMapper.updateById(albumInfo);

		//根据专辑id删除标签名称和标签值数据
		LambdaQueryWrapper<AlbumAttributeValue> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(AlbumAttributeValue::getAlbumId,albumId);
		albumAttributeValueMapper.delete(wrapper);

		//添加专辑下面 标签名称和标签值数据
		List<AlbumAttributeValueVo> albumAttributeValueVoList =
				                 albumInfoVo.getAlbumAttributeValueVoList();
		if(!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
			albumAttributeValueVoList.stream().forEach(albumAttributeValueVo -> {
				AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
				BeanUtils.copyProperties(albumAttributeValueVo,albumAttributeValue);
				albumAttributeValue.setAlbumId(albumId);
				albumAttributeValueMapper.insert(albumAttributeValue);
			});
		}
	}

	//获取当前用户专辑列表
	//默认查询前100个专辑
	@Override
	public List<AlbumInfo> findUserAllAlbumList(Long userId) {
		//创建page对象，传入当前页和每页记录数
		Page<AlbumInfo> pageParam = new Page<>(1, 100);
		// select id,album_title from album_info where user_id=1 order by id desc

		LambdaQueryWrapper<AlbumInfo> wrapper = new LambdaQueryWrapper<>();
		//指定查询字段
		wrapper.select(AlbumInfo::getId,AlbumInfo::getAlbumTitle);
		//根据userId查询
		wrapper.eq(AlbumInfo::getUserId,userId);
		//排序 专辑id desc
		wrapper.orderByDesc(AlbumInfo::getId);

		//调用selectPage方法实现分页查询，第一个参数Page对象，第二个参数条件对象
		IPage<AlbumInfo> albumInfoPage =
				albumInfoMapper.selectPage(pageParam, wrapper);

		//从IPage对象里面获取数据list集合
		List<AlbumInfo> list = albumInfoPage.getRecords();

		return list;
	}

	//远程调用：根据专辑id获取四个统计数据
	@Override
	public Map<String, Object> getAlbumInfoStat(Long albumId) {
		return albumStatMapper.getAlbumInfoStat(albumId);
	}

	//根据专辑Id 获取到专辑属性列表
	@Override
	public List<AlbumAttributeValue> findAlbumAttributeValueByAlbumId(Long albumId) {
		LambdaQueryWrapper<AlbumAttributeValue> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(AlbumAttributeValue::getAlbumId,albumId);

		List<AlbumAttributeValue> albumAttributeValueList =
				albumAttributeValueMapper.selectList(wrapper);
		//	返回集合数据
		return albumAttributeValueList;
	}

	@Override
	public AlbumStatVo getAlbumStatVoByAlbumId(Long albumId) {

		return albumStatMapper.getAlbumStatVoByAlbumId(albumId);
	}

	//保存专辑统计数据的方法
	public void saveAlbumStat(Long albumId, String statType) {
		AlbumStat albumStat = new AlbumStat();
		albumStat.setAlbumId(albumId);
		albumStat.setStatType(statType);// 0401  0402 ....0403
		albumStat.setStatNum(0);
		albumStatMapper.insert(albumStat);
	}

	//批量添加方法
	@Autowired
	private AlbumAttributeValueService albumAttributeValueService;

	public void saveAlbumInfo1(AlbumInfoVo albumInfoVo) {
		//1 添加专辑基本信息 album_info
		AlbumInfo albumInfo = new AlbumInfo();
		// AlbumInfoVo 值--放到 AlbumInfo
		//String albumTitle = albumInfoVo.getAlbumTitle();
		//albumInfo.setAlbumTitle(albumTitle);
		BeanUtils.copyProperties(albumInfoVo,albumInfo);

		//专辑有几个值需要单独设置，前端没有传递过来的
		//TODO  userId 用户id，后面完善
		albumInfo.setUserId(1L);
		// 专辑状态，设置通过
		albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
		//设置收费专辑，免费试听集数，前3集
		String payType = albumInfo.getPayType();
		if(!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(payType)) {
			albumInfo.setTracksForFree(3);
		}
		//调用方法保存
		albumInfoMapper.insert(albumInfo);

		//2 添加专辑下面标签名称和标签值数据 album_attribute_value
		List<AlbumAttributeValueVo> albumAttributeValueVoList =
				albumInfoVo.getAlbumAttributeValueVoList();

		// List<AlbumAttributeValueVo> -- List<AlbumAttributeValue>
		List<AlbumAttributeValue> albumAttributeValueList =
				albumAttributeValueVoList.stream().map(albumAttributeValueVo -> {

			AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
			BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
			albumAttributeValue.setAlbumId(albumInfo.getId());
			return albumAttributeValue;

		}).collect(Collectors.toList());

		//批量添加
		albumAttributeValueService.saveBatch(albumAttributeValueList);


		//3 添加专辑四个统计数据 播放量，订阅量等 初始值 0 album_stat
		//播放量
		this.saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_PLAY);
		//订阅量
		this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_SUBSCRIBE);
		//购买量
		this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_BROWSE);
		//评论数
		this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_COMMENT);
	}

////////////////////////////////////////////////////
///////////////////////////
///使用setnx + 过期时间 + uuid + lua脚本
	public AlbumInfo getAlbumInfo2(Long albumId) {
		// "album:info:"+1
		String albumKey = RedisConstant.ALBUM_INFO_PREFIX+albumId;
		//1 根据redis里面key：专辑id查询redis
		AlbumInfo albumInfo = (AlbumInfo)redisTemplate.opsForValue().get(albumKey);
		//2 如果没有查询到数据
		if(albumInfo == null) {
			//2.0 添加分布式锁解决缓存击穿问题
			// setnx加锁 + uuid + 过期时间 + LUA脚本
			String lockKey = RedisConstant.ALBUM_INFO_PREFIX+albumId+ ":lock";
			String uuid = UUID.randomUUID().toString();
			Boolean ifAbsent = redisTemplate.opsForValue()
					.setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
			if(ifAbsent) { //加锁成功
				try {
					//2.1 根据专辑id查询mysql，把数据返回，并且把数据放到redis里面
					AlbumInfo albumInfoData = this.getAlbumInfoData(albumId);

					if (null == albumInfoData){
						//	设置控制存储到缓存
						AlbumInfo albumInfo1 = new AlbumInfo();
						redisTemplate.opsForValue().set(albumKey,albumInfo1,RedisConstant.ALBUM_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);
						return albumInfo1;
					}

					//把查询mysql数据放到redis里面
					redisTemplate.opsForValue().set(albumKey,albumInfoData,
							10, TimeUnit.MINUTES);
					return albumInfoData;
				}finally {
					//解锁,lua脚本
					delRedisKey2(lockKey, uuid);
				}
			} else { //没有获取锁
				//自旋
				//	没有获取到锁的线程，自旋
				return getAlbumInfo(albumId);
			}
		} else {//3 如果查询到数据，直接返回
			return albumInfo;
		}
	}

	//解锁
	private void delRedisKey2(String lockKey, String uuid) {
		DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
		String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
				"then\n" +
				"    return redis.call(\"del\",KEYS[1])\n" +
				"else\n" +
				"    return 0\n" +
				"end";
		redisScript.setScriptText(script);
		redisScript.setResultType(Long.class);
		redisTemplate.execute(redisScript, Arrays.asList(lockKey),uuid);
	}

//////////////////////////////////////////
///使用Redisson实现分布式锁
	@Autowired
	private RedissonClient redissonClient;

	//使用Redisson实现分布式锁
	public AlbumInfo getAlbumInfoRedisson(Long albumId) throws Exception {
		String albumKey = RedisConstant.ALBUM_INFO_PREFIX+albumId;
		//1 查询redis
		AlbumInfo albumInfo =
				  (AlbumInfo)redisTemplate.opsForValue().get(albumKey);

		//2 如果没有查询，查询mysql，返回数据，放到redis里面
		if(albumInfo == null) {
			// 缓存击穿问题，添加分布式锁
			String lockKey = RedisConstant.ALBUM_INFO_PREFIX+albumId+ ":lock";
			//获取锁对象
			RLock rLock  = redissonClient.getLock(lockKey);
			//加锁
			boolean tryLock = rLock.tryLock(3,TimeUnit.SECONDS);

			if(tryLock) { //加锁成功
				try {
					AlbumInfo albumInfoData = this.getAlbumInfoData(albumId);

					redisTemplate.opsForValue().set(albumKey,albumInfoData,
							10,TimeUnit.MINUTES);
					return albumInfoData;
				}finally {
					//解锁
					rLock.unlock();
				}

			} else {
				//自旋
				this.getAlbumInfoRedisson(albumId);
			}
		} else {
			//3 如果查询到数据，直接返回
			return albumInfo;
		}
		return null;
	}

}
